package org.simplesocket.simplenettycore.handlers;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageCodec;
import lombok.extern.slf4j.Slf4j;
import org.simplesocket.simplenettycore.protocol.SimpleProtocol;
import org.simplesocket.simplenettycore.utils.CommonUtils;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

@Slf4j
public class ProtocolCodecHandler extends ByteToMessageCodec<SimpleProtocol> {
    
    @Override
    protected void encode(ChannelHandlerContext ctx, SimpleProtocol sp, ByteBuf out) {
        int magicNumber = SimpleProtocol.MAGIC_NUMBER;
        byte type = sp.getType().getValue();
        byte status = sp.getStatus().getValue();
        SimpleProtocol.Type typeEnum = SimpleProtocol.Type.byValue(type);
        assert typeEnum != null;
        try {
            byte[] sessionIDBytes = sp.getSessionID().getBytes(StandardCharsets.UTF_8);
            byte[] uriBytes = sp.getUri().getBytes(StandardCharsets.UTF_8);
            byte[] dataBytes = sp.getData().getBytes(StandardCharsets.UTF_8);
            
            int length = 4 + 1 + 1 + sessionIDBytes.length + 4 + uriBytes.length + dataBytes.length;
            
            out.writeInt(length);
            out.writeInt(magicNumber);
            out.writeByte(type);
            out.writeByte(status);
            out.writeBytes(sessionIDBytes);
            out.writeInt(uriBytes.length);
            out.writeBytes(uriBytes);
            out.writeBytes(dataBytes);
        }catch (Exception e){
            log.error("Encode client '{}' data error,sessionID is '{}',\n",
                    ctx.channel().remoteAddress(),sp.getSessionID());
            CommonUtils.printException(e);
            
            ProtocolDataWriter.writeDataBack(ctx, SimpleProtocol.builder()
                            .type(sp.getType())
                            .status(SimpleProtocol.Status.ERROR)
                            .sessionID(sp.getSessionID())
                            .uri(sp.getUri())
                            .data(JSON.toJSONString(Map.of("sessionID","Encode data error")))
                    .build());
        }finally {
            sp.recycle();
        }
    }
    
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
        in.readInt();// 去除长度
        in.readInt();// 去除魔数
        SimpleProtocol.Type type = SimpleProtocol.Type.byValue(in.readByte());// 读取类型
        in.readByte();// 去除状态，服务端不需要状态
        assert type != null;
        
        String sessionID = "";
        String uri = "";
        try {
            sessionID = in.readBytes(32).toString(StandardCharsets.UTF_8);// 读取sessionID
            int uriLength = in.readInt();// 读取uri长度
            uri = in.readBytes(uriLength).toString(StandardCharsets.UTF_8);// 读取uri
            String bodyStr = in.readBytes(in.readableBytes()).toString(StandardCharsets.UTF_8);
            
            out.add(SimpleProtocol.builder()
                            .type(type)
                            .sessionID(sessionID)
                            .uri(uri)
                            .data(bodyStr)
                    .build());
        }catch (Exception e){
            log.error("Decode client '{}' data error:\n",ctx.channel().remoteAddress());
            CommonUtils.printException(e);
            
            if (StrUtil.isBlank(uri)){
                // 直接关闭连接吧，这里返回也没有意义
                ctx.close();
            }
            ProtocolDataWriter.writeDataBack(ctx,SimpleProtocol.builder()
                            .type(type)
                            .status(SimpleProtocol.Status.ERROR)
                            .sessionID(sessionID)
                            .uri(uri)
                            .data(JSON.toJSONString(Map.of("sessionID","Decode data error")))
                    .build());
        }
    }
    
}
